﻿/* Copyright (C) Siemens AG, 2012. All Rights Reserved.
 * Author: Roland Eckl, Corporate Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using RVM;
using RVM.Infrastructure.Communication;
using RVM.Infrastructure.Communication.WCF.NetPipe;
using RVM.Infrastructure.Identification;
using RVM.Infrastructure.Identification.Process;
using RvmShared;


namespace RvmImporter
{

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Private Members

        /// <summary>
        /// The identification provider allows to query for available exporters. Here the ID is the process ID.
        /// Furthermore it throws an event when a new exporter shows up.
        /// </summary>
        private IIdentificationProvider _exporterObserver = new ProcessIdentificationProvider(SimpleSession.Current, SimpleSession.ProcessName);

        #endregion

        #region Public Members

        /// <summary>
        /// Collection of imported ViewModels.
        /// </summary>
        public ObservableCollection<INotifyPropertyChanged> Importers = new ObservableCollection<INotifyPropertyChanged>();

        /// <summary>
        /// List of RemotePartners, means a list of Importers and the imported ViewModel.
        /// This list assumes that every importer imports only a single view model.
        /// </summary>
        public List<RemotePartner> Partners = new List<RemotePartner>();

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor. Wires up events and UI.
        /// </summary>
        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
            Imports.ItemsSource = Importers;
            _exporterObserver.ExportingSource += OnExportingSourcesChanged;

            OnConnectAllClick(null, null);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handler that is called whenever the observing identification provider realizes a new exporting process.
        /// </summary>
        private void OnExportingSourcesChanged()
        {
            // Simply dispatch into UI thread.
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
                delegate()
                    {
                        OnRefreshProcessesClick(this, new RoutedEventArgs());
                        OnConnectAllClick(this, new RoutedEventArgs());
                    }
                ));
        }

        /// <summary>
        /// Handler that is raised when some importer runs into faulted state.
        /// The imported viewmodel must be removed and collections cleaned up.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>        
        void OnImporterInFaultedState(object sender, EventArgs e)
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
                delegate
                    {
                        RemotePartner partner = (from p in Partners where p.Importer.Equals(sender) select p).First();

                        Importers.Remove(partner.ViewModel);
                        
                        partner.ViewModel = null;
                        partner.Importer = null;
                        Partners.Remove(partner);
                        partner = null;
                    }
                ));
        }

        /// <summary>
        /// Handler when user pressed the Refresh button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnRefreshProcessesClick(object sender, RoutedEventArgs e)
        {
            PList.Items.Clear();
            string[] res = _exporterObserver.GetExportingIdentities();
            foreach(string r in res)
            {
                PList.Items.Add(r);
            }
        }

        /// <summary>
        /// Handler when user pressed Connect button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConnectClick(object sender, RoutedEventArgs e)
        {
            // Some source selected?
            if(PList.SelectedItem == null)
            {
                MessageBox.Show("No exporting source selected!");
                return;
            }

            string id = PList.SelectedItem.ToString();

            // Already connected?
            if((from p in Partners where p.Importer != null && p.Importer.Id.Equals(id) select p).Any())
            {
                MessageBox.Show("Already connected to this source!");
                return;
            }

            Connect(id);
        }

        /// <summary>
        /// Handler when user pressed Disconnect button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDisconnectClick(object sender, RoutedEventArgs e)
        {
            if (Imports.SelectedIndex < 0)
            {
                MessageBox.Show("No Import (=Tab) selected!");
                return;
            }

            RemotePartner partner = (from p in Partners where p.ViewModel.Equals(Imports.SelectedItem) select p).First();

            if (partner == null)
                return;

            partner.Importer.Faulted -= OnImporterInFaultedState;
            if (partner.Importer != null && partner.Importer.Connected)
                partner.Importer.Stop();

            Importers.Remove(partner.ViewModel);
        
            partner.ViewModel = null;
            partner.Importer = null;
            Partners.Remove(partner);
            partner = null;
        }

        /// <summary>
        /// Handler when the user pressed the Connect All button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConnectAllClick(object sender, RoutedEventArgs e)
        {
            OnRefreshProcessesClick(sender, null);
            foreach (string id in PList.Items)
            {
                // Already connected?
                if (!(from p in Partners where p.Importer != null && p.Importer.Id.Equals(id) select p).Any())
                {
                    Connect(id);
                }
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Connects to an exporting party, imports the viewmodel and adds importer+viewmodel to list.
        /// The ViewModel is also put into the observable collection then.
        /// </summary>
        /// <param name="id"></param>
        private void Connect(string id)
        {
            RemotePartner partner = new RemotePartner();
            partner.Importer = new NetPipeViewModelImporter(id, SimpleSession.Current);
            partner.Importer.Faulted += new EventHandler(OnImporterInFaultedState);
            partner.Importer.Start();
            if (partner.Importer.Connected)
            {
                partner.ViewModel = partner.Importer.GetViewModel(typeof(ViewModelParent),"EntryPoint");
                ((ViewModelParent)partner.ViewModel).Simple.Id = id;
                Importers.Add(partner.ViewModel);
                Imports.SelectedItem = partner.ViewModel;
                Partners.Add(partner);
            }
        }

        #endregion

        private void OnStartNewProcessClick(object sender, RoutedEventArgs e)
        {
            string dir = Directory.GetCurrentDirectory();

            Process process = new Process();
            process.StartInfo.FileName = dir + @"\RvmExporter.exe";
            process.StartInfo.UseShellExecute = true;
            process.StartInfo.CreateNoWindow = false;
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

            process.Start(); 

        }
    }

    /// <summary>
    /// Simply class to combine an importer and a single view model.
    /// </summary>
    public class RemotePartner
    {
        public IViewModelImporter Importer { get; set; }

        public RemotableViewModel ViewModel { get; set; }
    }

}
